package com.mm.cloud.common.redis.aspect;

import com.mm.cloud.common.redis.annotation.DistributionLock;
import com.mm.cloud.common.redis.constant.RedisKeySystemConstants;
import com.mm.cloud.common.redis.util.RedisKeyStringUtils;
import com.mm.cloud.common.core.exception.BaseException;
import com.mm.cloud.common.core.util.AspectHandleUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 分布式锁切面
 *
 * @author mr
 * @since 2022-01-04
 */
@Aspect
@Component
public class DistributionLockAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.mm.cloud.common.redis.annotation.DistributionLock)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DistributionLock distributionLock = method.getAnnotation(DistributionLock.class);
        String[] paramNames = distributionLock.paramName().split(":");
        StringBuilder builder = new StringBuilder();
        Object param;
        for (int i = 0; i < paramNames.length; i++) {
            param = AspectHandleUtils.parser(paramNames[i], point.getArgs(), method, Object.class);
            builder.append(null == param ? "" : param.toString());
        }
        String lockKey = RedisKeyStringUtils.appendRedisKey(RedisKeySystemConstants.REDIS_DISTRIBUTION_LOCK, distributionLock.key(), builder.toString());
        RLock lock = redissonClient.getLock(lockKey);
        boolean tryLock = true;
        try {
            if (distributionLock.isReturn()) {
                if (!(tryLock = lock.tryLock())) {
                    throw new BaseException("当前操作没有执行完，请稍微刷新再试，防止产生重复数据！");
                }
            } else {
                lock.lock();
            }
            return point.proceed();
        } finally {
            if (tryLock) {
                lock.unlock();
            }
        }
    }


}
